
// import * as d3 from "../lib/d3.js";
import * as d3 from "d3";
import {choke} from "../util/functions.js";
import {$} from "../util/seq.js";

const THRESHOLD = 5;  // Use a threshold to check if there is enough scrollable space of consequence.

/**
 * @param {HTMLElement|SVGElement} node
 * @returns {string[]}
 */
function scrollableDirectionsFor(node) {
    return [
        node.scrollTop > THRESHOLD ? "up" : null,
        node.scrollHeight - node.clientHeight - node.scrollTop > THRESHOLD ? "down" : null,
    ].filter(e => e !== null);
}

/**
 * @param {HTMLElement|SVGElement} node
 */
function updateScrollableAttr(node) {
    const dir = scrollableDirectionsFor(node);
    if (dir.length > 0) {
        node.dataset.scrollable = dir.join(" ");
    } else {
        delete node.dataset.scrollable;
    }
}

const WAIT = 100;  // Choke the update events using a wait of 100 ms for performance.
const updateScrollableAttr_choked = choke(updateScrollableAttr, WAIT);

/**
 * Keeps the data-scrollable attribute in sync with the scrollability of the element.
 *
 *    data-scrollable="up"       the element can be scrolled up
 *    data-scrollable="down"     the element can be scrolled down
 *    data-scrollable="up down"  the element can be scrolled both up and down
 *    [no attr]                  the element is not scrollable
 *
 * @param element a d3 selection of a single element to observe and update
 */
export function defineScrollableUpdater(element) {
    const node = element.node();
    const update = () => updateScrollableAttr_choked(node);

    element.on($`scroll`, update);

    if (window.ResizeObserver) {
        new ResizeObserver(update).observe(node);
    } else {
        new MutationObserver(update).observe(node, {
            attributes: true,
            attributeFilter: ["hidden"],
            childList: true,
            subtree: true,
        });
        d3.select(window).on($`resize`, update);
    }
}
